package com.gzx.asr.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import javax.sound.sampled.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 * 左右声道分离
 */
@Slf4j
public class AudioSplitterUtil {

    public static void main(String[] args) {
        String url = "C:\\Users\\Baolo\\Desktop\\623";

        //扫描指定目录下的所有 .wav 文件
        List<String> paths = scanWavFiles(url);

        //批量交换左右声道
        batchSwapChannels(paths);

        //删除左右声道音频
        deleteLeftAndRightChannelFiles(url);

    }

    //cmd强制删除
    public static void deleteWithCmdForce(String filePath) {
        try {
            String cmd = String.format("cmd /c del /f /q \"%s\"", filePath);
            Process p = Runtime.getRuntime().exec(cmd);
            int exitCode = p.waitFor();

            if (exitCode == 0) {
                System.out.println("[CMD] 强制删除成功: " + filePath);
            } else {
                System.err.println("[CMD] 删除失败，尝试解锁后删除...");
                unlockAndDelete(filePath); // 见步骤3
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //释放文件锁
    public static void unlockAndDelete(String filePath) {
        try {
            // 步骤1：查找锁定进程
            Process find = Runtime.getRuntime().exec(
                    "handle64.exe -p \"" + filePath + "\""
            );
            find.waitFor();

            // 解析输出获取PID（需处理输出流）
            // 步骤2：强制结束进程
            Runtime.getRuntime().exec("taskkill /pid [PID] /f").waitFor();

            // 步骤3：再次尝试删除
            Files.delete(Paths.get(filePath));
        } catch (Exception e) {
            System.err.println("解锁删除失败: " + e.getMessage());
        }
    }


    /**
     * 删除指定目录下所有 _left_channel.wav 和 _right_channel.wav 文件
     *
     * @param directoryPath 目标目录路径
     */
    public static void deleteLeftAndRightChannelFiles(String directoryPath) {
        File directory = new File(directoryPath);

        if (!directory.exists() || !directory.isDirectory()) {
            System.out.println("目录不存在或不是文件夹: " + directoryPath);
            return;
        }

        // 使用 listFiles() 遍历并筛选文件
        File[] filesToDelete = directory.listFiles((dir, name) ->
                name.endsWith("channel.wav")
        );

        if (filesToDelete != null) {
            for (File file : filesToDelete) {
                deleteWithCmdForce(file.getAbsolutePath());
            }
        }
    }

    //使用power shell强制删除
    public static void deleteWithPowerShell(String filePath) {
        try {
            // 构建 PowerShell 命令（强制删除且不提示）
            String command = "powershell -command \"Remove-Item -Path '" +
                    filePath.replace("\\", "\\\\") +
                    "' -Force -ErrorAction SilentlyContinue\"";

            Process process = Runtime.getRuntime().exec(command);
            int exitCode = process.waitFor();

            if (exitCode == 0) {
                System.out.println("PowerShell 删除成功: " + filePath);
            } else {
                System.err.println("PowerShell 删除失败 (退出码 " + exitCode + "): " + filePath);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 扫描指定目录下所有的 .wav 文件全路径
     *
     * @param directoryPath 目录路径
     * @return wav文件的全路径列表
     */
    public static List<String> scanWavFiles(String directoryPath) {
        List<String> wavFiles = new ArrayList<>();

        try {
            Path startPath = Paths.get(directoryPath);
            if (!Files.exists(startPath)) {
                System.out.println("目录不存在: " + directoryPath);
                return wavFiles;
            }

            Files.walk(startPath)
                    .filter(path -> path.toString().toLowerCase().endsWith(".wav"))
                    .forEach(path -> wavFiles.add(path.toAbsolutePath().toString()));

        } catch (IOException e) {
            System.err.println("扫描目录时发生错误: " + e.getMessage());
        }

        return wavFiles;
    }

    //批量交换左右声道
    public static void batchSwapChannels(List<String> folderPath) {
        if (!ObjectUtils.isEmpty(folderPath)) {
            folderPath.forEach(path -> {
                // 拆分左右声道
                splitTwoVoice(path);

                // 获取拆分后文件路径
                String filePath = FileUtils.removeWavExtension(path);
                String outputLeft = filePath + "_left_channel.wav";
                String outputRight = filePath + "_right_channel.wav";

                // 合并后的文件放到 resource 目录下
                String resourceDir = new File(path).getParent() + File.separator + "result";
                String mergedOutput = resourceDir + File.separator + new File(path).getName();
                mergedOutput = FileUtils.removeWavExtension(mergedOutput) + ".wav";

                // 创建 resource 目录（如果不存在）
                File dir = new File(resourceDir);
                if (!dir.exists()) {
                    dir.mkdirs();
                }

                // 合并并交换声道
                mergeTwoChannels(outputLeft, outputRight, mergedOutput);
            });
        }
    }


    //拆分左右声道音频
    public static void splitTwoVoice(String url) {
        if (ObjectUtils.isEmpty(url)) {
            return;
        }
        String filePath = FileUtils.removeWavExtension(url);
        String outputLeft = filePath + "_left_channel.wav";
        String outputRight = filePath + "_right_channel.wav";

        try {
            // 读取原始音频文件：音频没有内容的这里会报错
            AudioInputStream originalStream = AudioSystem.getAudioInputStream(new File(url));
            AudioFormat originalFormat = originalStream.getFormat();

            // 验证是否为双声道
            if (originalFormat.getChannels() != 2) {
                log.warn("输入文件不是双声道音频 {} ", originalFormat.getChannels());
                return;
            }

            // 读取所有音频数据
            byte[] bytes = new byte[(int) (originalStream.getFrameLength() * originalFormat.getFrameSize())];
            originalStream.read(bytes);

            // 分离左右声道数据
            ByteBuffer buffer = ByteBuffer.wrap(bytes).order(
                    originalFormat.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN
            );

            byte[] leftBytes = new byte[bytes.length / 2];
            byte[] rightBytes = new byte[bytes.length / 2];

            int l = 0, r = 0;
            while (buffer.remaining() >= 4) {
                // 左声道样本（2 bytes）
                leftBytes[l++] = buffer.get();
                leftBytes[l++] = buffer.get();

                // 右声道样本（2 bytes）
                rightBytes[r++] = buffer.get();
                rightBytes[r++] = buffer.get();
            }

            // 创建单声道音频格式
            AudioFormat monoFormat = new AudioFormat(
                    originalFormat.getEncoding(),
                    originalFormat.getSampleRate(),
                    originalFormat.getSampleSizeInBits(),
                    1,  // 单声道
                    originalFormat.getFrameSize() / 2,
                    originalFormat.getFrameRate(),
                    originalFormat.isBigEndian()
            );

            // 保存左声道
            saveChannel(outputLeft, monoFormat, leftBytes);
            // 保存右声道
            saveChannel(outputRight, monoFormat, rightBytes);

            log.info("拆分完成: {}", outputLeft + " 和 " + outputRight);

        } catch (UnsupportedAudioFileException e) {
            //这里不一定是拆分的时候报错了，大多数情况都是文件大小不足1m，音频内容为空导致的
            log.warn("输入文件不是双声道音频");
        } catch (IOException e) {
            log.error("拆分左右声道音频处理失败: ", e);
        }
    }

    /**
     * 将两个单声道音频文件合并为一个双声道音频文件，并交换左右声道顺序
     *
     * @param leftChannelPath  原左声道文件路径
     * @param rightChannelPath 原右声道文件路径
     * @param outputPath       合并后的输出文件路径
     */
    public static void mergeTwoChannels(String leftChannelPath, String rightChannelPath, String outputPath) {
        try {
            // 读取左右声道音频流
            AudioInputStream leftStream = AudioSystem.getAudioInputStream(new File(leftChannelPath));
            AudioInputStream rightStream = AudioSystem.getAudioInputStream(new File(rightChannelPath));

            AudioFormat format = leftStream.getFormat();

            // 确保是单声道格式
            if (format.getChannels() != 1) {
                log.warn("声道必须是单声道");
                return;
            }

            // 读取左右声道数据
            byte[] leftData = readAllBytes(leftStream);
            byte[] rightData = readAllBytes(rightStream);

            // 创建交错字节数组：先写入右声道，再写入左声道，实现“交换”
            byte[] mergedData = new byte[leftData.length + rightData.length];
            for (int i = 0; i < leftData.length / 2; i++) {
                int offset = i * 4;
                // 先写入右声道样本
                mergedData[offset] = rightData[i * 2];
                mergedData[offset + 1] = rightData[i * 2 + 1];
                // 再写入左声道样本
                mergedData[offset + 2] = leftData[i * 2];
                mergedData[offset + 3] = leftData[i * 2 + 1];
            }

            // 构建双声道格式
            AudioFormat stereoFormat = new AudioFormat(
                    format.getEncoding(),
                    format.getSampleRate(),
                    format.getSampleSizeInBits(),
                    2, // 双声道
                    format.getFrameSize() * 2,
                    format.getFrameRate(),
                    format.isBigEndian()
            );

            // 创建合成音频流
            AudioInputStream mergedStream = new AudioInputStream(
                    new ByteArrayInputStream(mergedData),
                    stereoFormat,
                    mergedData.length / stereoFormat.getFrameSize()
            );

            // 写出到文件
            AudioSystem.write(mergedStream, AudioFileFormat.Type.WAVE, new File(outputPath));

            log.info("声道交换并合并完成: {}", outputPath);

        } catch (UnsupportedAudioFileException | IOException e) {
            log.error("合并声道失败", e);
        }
    }

    /**
     * 辅助方法：读取音频流中的所有字节
     */
    private static byte[] readAllBytes(AudioInputStream stream) throws IOException {
        byte[] buffer = new byte[8192];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int bytesRead;
        while ((bytesRead = stream.read(buffer)) != -1) {
            baos.write(buffer, 0, bytesRead);
        }
        return baos.toByteArray();
    }

    //保存拆分后的单声道音频
    public static void saveChannel(String path, AudioFormat format, byte[] data) throws IOException {
        try (AudioInputStream stream = new AudioInputStream(
                new ByteArrayInputStream(data),
                format,
                data.length / format.getFrameSize()
        )) {
            AudioSystem.write(stream, AudioFileFormat.Type.WAVE, new File(path));
        }
    }
}
